package com.github.chirspan.xaas.core.db.jdbc;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.HashUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.chirspan.xaas.core.model.DbDataSource;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * @className: JdbcDataProvider
 * @description: <p>JDBC 数据库</p>
 * @author: chenpan
 * @date: 2021/4/21 14:05
 **/
public class JdbcDataProvider {

    private static final String DRIVER = "driver";

    private static final String JDBC_URL = "jdbcUrl";

    private static final String USERNAME = "username";

    private static final String PASSWORD = "password";

    private static final String POOLED = "usePool";

    private static final ConcurrentMap<String, DataSource> DATASOURCE_MAP = new ConcurrentHashMap<>();

    public static Connection getConnection(DbDataSource datasource) throws Exception {
        Map<String, Object> params = JSON.parseObject(datasource.getDsConfig());
        return getConnection(params);
    }

    public static Connection getConnection(Map<String, Object> params) throws Exception {
        boolean usePool = MapUtil.getBool(params, POOLED);
        Connection conn = null;
        if (usePool) {
            DataSource ds = getDataSource(params);
            String key = String.valueOf(HashUtil.fnvHash(JSONObject.toJSON(params).toString()));
            try {
                conn = ds.getConnection();
            } catch (SQLException e) {
                DATASOURCE_MAP.remove(key);
                throw e;
            }
            return conn;
        } else {
            String username = MapUtil.getStr(params, USERNAME);
            String password = MapUtil.getStr(params, PASSWORD);
            String driver = MapUtil.getStr(params, DRIVER);
            String jdbcUrl = MapUtil.getStr(params, JDBC_URL);
            Class.forName(driver);
            Properties props = new Properties();
            props.setProperty("user", username);
            if (StrUtil.isNotBlank(password)) {
                props.setProperty("password", password);
            }
            return DriverManager.getConnection(jdbcUrl, props);
        }
    }

    public static DataSource getDataSource(Map<String, Object> dataSource) {
        boolean usePool = MapUtil.getBool(dataSource, POOLED);
        String username = MapUtil.getStr(dataSource, USERNAME);
        String password = MapUtil.getStr(dataSource, PASSWORD);
        String driver = MapUtil.getStr(dataSource, DRIVER);
        String jdbcUrl = MapUtil.getStr(dataSource, JDBC_URL);
        String key = String.valueOf(HashUtil.fnvHash(JSONObject.toJSON(dataSource).toString()));
        DataSource ds = DATASOURCE_MAP.get(key);
        if (ObjectUtil.isEmpty(ds)) {
            if (ds == null) {
                synchronized (key.intern()) {
                    ds = DATASOURCE_MAP.get(key);
                    if (ds == null) {
                        HikariConfig conf = new HikariConfig();
                        conf.setDriverClassName(driver);
                        conf.setJdbcUrl(jdbcUrl);
                        conf.setUsername(username);
                        if (StrUtil.isNotBlank(password)) {
                            conf.setPassword(password);
                        }
                        HikariDataSource hikariDs = new HikariDataSource(conf);
                        DATASOURCE_MAP.put(key, hikariDs);
                        ds = DATASOURCE_MAP.get(key);
                    }
                }
            }

        }
        return ds;
    }

    public static DataSource getDataSource(DbDataSource datasource) throws Exception {
        Map<String, Object> params = JSON.parseObject(datasource.getDsConfig());
        return getDataSource(params);
    }

    public static List<LinkedHashMap<String, Object>> queryData(DbDataSource datasource, String sql)
            throws Exception {
        return DBUtils.queryData(getConnection(datasource), sql);
    }

    public static Object querySimpleData(DbDataSource datasource, String sql)
            throws Exception {

        return DBUtils.querySimpleData(getConnection(datasource), sql);
    }

    public static void execUpdate(DbDataSource datasource, String sql)
            throws Exception {

        DBUtils.execUpdate(getConnection(datasource), sql);
    }

    public static List<String> getMetaData(DbDataSource datasource, String sql) throws Exception {
        return DBUtils.getMetaDataBySql(getConnection(datasource), sql);
    }

    public static JSONArray getMetaData2(DbDataSource datasource, String sql) throws Exception {
        return DBUtils.getMetaDataBySql2(getConnection(datasource), sql);
    }

    public static boolean isDateColumn(String type) {
        String[] numberType = {"DATE", "DATETIME"};
        if (Arrays.asList(numberType).contains(type.toUpperCase())) {
            return true;
        }

        return false;
    }

    public static boolean isNumberColumn(String type) {
        String[] numberType = {"NUMBER", "LONG", "FLOAT", "DOUBLE", "DECIMAL", "TINYINT", "SMALLINT", "INT", "BIGINT"};
        if (Arrays.asList(numberType).contains(type.toUpperCase())) {
            return true;
        }

        return false;
    }
}
